#ifndef LOGGER_H
#define LOGGER_H

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "sink.hpp"
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "looper.hpp"
#include "message.hpp"
#include <mutex>
#include <atomic>
#include <cstdarg>
#include <unordered_map>

namespace Log
{
    class Logger
    {
    public:
        Logger(const std::string &logger_name,
               LogLevel::level &level,
               Formatter::ptr &formatter,
               std::vector<LogSink::ptr> &sinks) : _logger_name(logger_name),
                                                   _limit_level(level),
                                                   _formatter(formatter),
                                                   _sinks(sinks)
        {
        }

        using ptr = std::shared_ptr<Logger>;

        const std::string &getName()
        {
            return _logger_name;
        }

        // 完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串--然后进行落地输出
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::level::DEBUG < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cerr << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            LogMessage msg(LogLevel::level::DEBUG, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::level::INFO < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cerr << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            LogMessage msg(LogLevel::level::INFO, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::level::WARN < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cerr << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            LogMessage msg(LogLevel::level::WARN, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::level::ERROR < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cerr << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            LogMessage msg(LogLevel::level::ERROR, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::level::FATAL < _limit_level)
                return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cerr << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            LogMessage msg(LogLevel::level::FATAL, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }

    protected:
        // 落地输出
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::level> _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    // 同步日志器，直接写入磁盘
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
                   LogLevel::level &level,
                   Formatter::ptr &formatter,
                   std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, level, formatter, sinks)
        {
        }

    protected:
        void log(const char *data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    // 异步日志器
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    LogLevel::level &level,
                    Formatter::ptr &formatter,
                    std::vector<LogSink::ptr> &sinks,
                    AsyncType looper_type)
            : Logger(logger_name, level, formatter, sinks), _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type))
        {
        }

        void log(const char *data, size_t len) override
        {
            _looper->push(data, len);
        }

        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    enum LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder() : _looper_type(AsyncType::ASYNC_SAFE),
                          _logger_type(LoggerType::LOGGER_SYNC),
                          _limit_level(LogLevel::level::DEBUG) {}
        void buildLoggerType(LoggerType logger_type) { _logger_type = logger_type; }
        void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }
        void buildLoggerName(const std::string &logger_name) { _logger_name = logger_name; }
        void buildLoggerLevel(LogLevel::level limit_level) { _limit_level = limit_level; }
        void buildLoggerFormatter(const std::string &pattern) { _formatter = std::make_shared<Formatter>(pattern); }
        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args) { _sinks.push_back(SinkFactory::create<SinkType>(std::forward<Args>(args)...)); }
        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _looper_type;
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::level _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    // 局部
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            if (!_formatter.get())
                _formatter = std::make_shared<Formatter>();
            if (_sinks.empty())
                buildSink<StdoutSink>();
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    // 日志器管理类
    class LoggerManager
    {
    public:
        static LoggerManager &getInstance()
        {
            static LoggerManager instance;
            return instance;
        }

        void addLogger(Logger::ptr &logger)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->getName(), logger)); // 已存在，则不插入
        }

        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _loggers.find(name) != _loggers.end();
        }

        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _loggers.count(name) ? _loggers[name] : Logger::ptr();
        }

        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        LoggerManager()
        {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers["root"] = _root_logger;
        }

        ~LoggerManager() {}
        LoggerManager(const LoggerManager &) = delete;
        LoggerManager &operator=(const LoggerManager &) = delete;

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; // 默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    // 全局
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            if (!_formatter.get())
                _formatter = std::make_shared<Formatter>();
            if (_sinks.empty())
                buildSink<StdoutSink>();

            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}

#endif